package xin.petstore.framework.cipher;

import java.security.InvalidParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.Date;
import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RSAUtils {
    private static Logger logger = LoggerFactory.getLogger(RSAUtils.class);
    private static final String ALGORITHOM = "RSA";
    private static final int KEY_SIZE = 1024;
    private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();

    public static synchronized KeyPair generateKeyPair() {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", DEFAULT_PROVIDER);
            keyPairGen.initialize(1024, new SecureRandom(DateFormatUtils.format(new Date(), "yyyyMMdd").getBytes()));

            return keyPairGen.generateKeyPair();
        } catch (InvalidParameterException ex) {
            logger.error("KeyPairGenerator does not support a key length of 1024.", ex);
        } catch (NullPointerException ex) {
            logger.error("RSAUtils#KEY_PAIR_GEN is null, can not generate KeyPairGenerator instance.",
                    ex);
        } catch (NoSuchAlgorithmException ex) {
            logger.error("RSAUtils#ALGORITHOM is not, can not generate KeyPairGenerator instance.",
                    ex);
        }
        return null;
    }

    public static byte[] encrypt(PublicKey publicKey, byte[] data)
            throws Exception {
        Cipher ci = Cipher.getInstance("RSA", DEFAULT_PROVIDER);
        ci.init(1, publicKey);
        return ci.doFinal(data);
    }

    public static String encryptString(PublicKey publicKey, String plaintext) {
        if ((publicKey == null) || (plaintext == null)) {
            return null;
        }
        byte[] data = plaintext.getBytes();
        try {
            byte[] en_data = encrypt(publicKey, data);
            return new String(Hex.encodeHex(en_data));
        } catch (Exception ex) {
            logger.error(ex.getCause().getMessage());
        }
        return null;
    }

    public static byte[] decrypt(PrivateKey privateKey, byte[] data)
            throws Exception {
        Cipher ci = Cipher.getInstance("RSA", DEFAULT_PROVIDER);
        ci.init(2, privateKey);
        return ci.doFinal(data);
    }

    public static String decryptString(PrivateKey privateKey, String encrypttext) {
        if ((privateKey == null) || (StringUtils.isBlank(encrypttext))) {
            return null;
        }
        try {
            byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
            byte[] data = decrypt(privateKey, en_data);
            return new String(data);
        } catch (Exception ex) {
            logger.error(String.format("\"%s\" Decryption failed. Cause: %s", new Object[]{encrypttext, ex.getCause().getMessage()}));
        }
        return null;
    }

    public static String decryptStringJs(PrivateKey privateKey, String encrypttext) {
        if ((privateKey == null) || (StringUtils.isBlank(encrypttext))) {
            return null;
        }
        try {
            String text = decryptString(privateKey, encrypttext);
            return StringUtils.reverse(text);
        } catch (Exception ex) {
            logger.error(String.format("\"%s\" Decryption failed. Cause: %s", new Object[]{encrypttext, ex.getCause().getMessage()}));
        }
        return null;
    }
}
